/**
 * \file StringRef.h
 * \brief StringRef definition class
 * \author Edouard ROGE
 * \date 22 september 2010
 */

#ifndef _STRINGREF_
#define _STRINGREF_

#include "Core/Thread/Mutex.h"
#include "String.h"

/**
 * \class StringRef
 * advanced string class, buffer of each string is kept in memory,
 * if a StringRef object is instantiated, it will check if the buffer already exists
 * and in this case the new string object will have the same pointer.
 * The comparison and the affectation functions are faster with less used memory
 */
class StringRef
{
public:
	/**
	 * \fn StringRef();
	 * Default constructor
	 */
	StringRef();

	/**
	 * \fn StringRef(char * s);
	 * \param s buffer of char to copy
	 * Constructor with param
	 */
	StringRef(char * s);

	/**
	 * \fn StringRef(const char * s);
	 * \param s buffer of char to copy
	 * Constructor with param
	 */
	StringRef(const char * s);

	/**
	 * \fn StringRef(const String & _str);
	 * \param _str string to copy
	 * Constructor with param
	 */
	StringRef(const String & _str);

	/**
	 * \fn StringRef(const StringRef & _strRef);
	 * \param _strRef string to copy
	 * Copy constructor
	 */
	StringRef(const StringRef & _strRef);

	/**
	 * \fn ~StringRef();
	 * Destructor
	 */
	~StringRef();

	/**
	 * \fn uint32 GetLength() const;
	 * \return the length of the string (the final char '\\0' non included)
	 */
	uint32 GetLength() const;

	/**
	 * \fn operator const char * () const;
	 * \return the buffer of char
	 * casting operator
	 */
	operator const char * () const;

	/**
	 * \fn StringRef & operator = (char * _str);
	 * \param _str set the buffer to the string
	 * \return (*this)
	 */
	StringRef & operator = (char * _str);

	/**
	 * \fn StringRef & operator = (const char * _str);
	 * \param _str set the buffer to the string
	 * \return (*this)
	 */
	StringRef & operator = (const char * _str);

	/**
	 * \fn StringRef & operator = (const String & _str);
	 * \param _str replace the current string by this one
	 * \return (*this)
	 */
	StringRef & operator = (const String & _str);

	/**
	 * \fn StringRef & operator = (const StringRef & _str);
	 * \param _str replace the current string by this one
	 * \return (*this)
	 * both of these StringRef object have the same buffer address
	 */
	StringRef & operator = (const StringRef & _str);

	/**
	 * \fn friend String operator+ (const StringRef & _str1, const StringRef & _str2);
	 * \param _str1
	 * \param _str2
	 * \return new string with the 2 params concatenated
	 */
	friend String operator+ (const StringRef & _str1, const StringRef & _str2);

	/**
	 * \fn friend String operator+ (const StringRef & _str1, const String & _str2);
	 * \param _str1
	 * \param _str2
	 * \return new string with the 2 params concatenated
	 */
	friend String operator+ (const StringRef & _str1, const String & _str2);

	/**
	 * \fn friend String operator+ (const String & _str1, const StringRef & _str2);
	 * \param _str1
	 * \param _str2
	 * \return new string with the 2 params concatenated
	 */
	friend String operator+ (const String & _str1, const StringRef & _str2);

	/**
	 * \fn friend bool operator== (const StringRef & _str1, const StringRef & _str2);
	 * \param _str1 first string to compare
	 * \param _str2 second string to compare
	 * \return true if _str1 and _str2 are equals
	 */
	friend bool operator== (const StringRef & _str1, const StringRef & _str2);

	/**
	 * \fn friend bool operator!= (const StringRef & _str1, const StringRef & _str2);
	 * \param _str1 first string to compare
	 * \param _str2 second string to compare
	 * \return true if _str1 and _str2 are not equals
	 */
	friend bool operator!= (const StringRef & _str1, const StringRef & _str2);

	/**
	 * \fn friend bool operator== (const StringRef & _str1, const String & _str2);
	 * \param _str1 first string to compare
	 * \param _str2 second string to compare
	 * \return true if _str1 and _str2 are equals
	 */
	friend bool operator== (const StringRef & _str1, const String & _str2);

	/**
	 * \fn friend bool operator!= (const StringRef & _str1, const String & _str2);
	 * \param _str1 first string to compare
	 * \param _str2 second string to compare
	 * \return true if _str1 and _str2 are not equals
	 */
	friend bool operator!= (const StringRef & _str1, const String & _str2);

	/**
	 * \fn friend bool operator== (const String & _str1, const StringRef & _str2);
	 * \param _str1 first string to compare
	 * \param _str2 second string to compare
	 * \return true if _str1 and _str2 are equals
	 */
	friend bool operator== (const String & _str1, const StringRef & _str2);

	/**
	 * \fn friend bool operator!= (const String & _str1, const StringRef & _str2);
	 * \param _str1 first string to compare
	 * \param _str2 second string to compare
	 * \return true if _str1 and _str2 are not equals
	 */
	friend bool operator!= (const String & _str1, const StringRef & _str2);

	/**
	 * \fn friend bool operator== (const StringRef & _str1, const char * _str2);
	 * \param _str1 first string to compare
	 * \param _str2 second string to compare
	 * \return true if _str1 and _str2 are equals
	 */
	friend bool operator== (const StringRef & _str1, const char * _str2);

	/**
	 * \fn friend bool operator!= (const StringRef & _str1, const char * _str2);
	 * \param _str1 first string to compare
	 * \param _str2 second string to compare
	 * \return true if _str1 and _str2 are not equals
	 */
	friend bool operator!= (const StringRef & _str1, const char * _str2);
		
	/**
	 * \fn friend bool operator== (const char * _str1, const StringRef & _str2);
	 * \param _str1 first string to compare
	 * \param _str2 second string to compare
	 * \return true if _str1 and _str2 are equals
	 */
	friend bool operator== (const char * _str1, const StringRef & _str2);

	/**
	 * \fn friend bool operator!= (const char * _str1, const StringRef & _str2);
	 * \param _str1 first string to compare
	 * \param _str2 second string to compare
	 * \return true if _str1 and _str2 are not equals
	 */
	friend bool operator!= (const char * _str1, const StringRef & _str2);

private:

	/**
	 * \fn static char * Insert(const String & _str);
	 * \param _str string added to the list
	 * \return the buffer address of this string
	 * \brief Check if the string exists and returns the corresponding address
	 */
	static char * Insert(const String & _str);

	/**
	 * \fn static void Remove(const char * _str);
	 * \param _str Buffer address to remove from the list
	 * \brief remove buffer
	 */
	static void Remove(const char * _str);

	/**
	 * \fn void Set(const String & _str);
	 * \param _str string to set
	 * \brief insert(if not present) and set the buffer
	 */
	void Set(const String & _str);

	/** Buffer address */
	char * m_Buffer;

	/** Static Mutex */
	static Mutex m_Mutex;

	/**
	 * \struct StringRefNode
	 * \brief structure used to store buffers with their ref count
	 */
	struct StringRefNode
	{
		/** Buffer's ref count */
		unsigned int m_nRef;

		/** String containing the buffer */
		String m_String;

		/** st_String Next in list */
		StringRefNode * m_pStringNext;

		/**
		 * \fn StringRefNode();
		 * \brief Default constructor
		 */
		StringRefNode();

		/**
		 * \fn StringRefNode(const char * _str);
		 * \param _str Buffer to copy
		 * \brief Set the ref count to 1
		 */
		StringRefNode(const char * _str);

		/**
		 * \fn StringRefNode(const String & _str);
		 * \param _str String to copy
		 * \brief Set the ref count to 1
		 */
		StringRefNode(const String & _str);

		/**
		 * \fn ~StringRefNode();
		 * \brief Destructor
		 */
		~StringRefNode();
	};

	/**  list used to store data (no vector because of reallocating when size is too small...) */
	static StringRefNode * m_pRootList;

};

#endif // _STRINGREF_
